package com.szeldon.declatest.declaration.reader;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.szeldon.declatest.declaration.TestsSuiteDeclaration;
import com.szeldon.declatest.declaration.reader.exceptions.TestSuiteFileException;
import com.szeldon.declatest.declaration.reader.settings.ISettingsReaderPlugin;

/**
 * Abstract superclass for all readers of files with declaration of test suites.
 * 
 * @author szeldon
 * 
 */
public abstract class TestsDeclarationReader {

    /** Name of file to read from. */
    protected String filename;

    /**
     * Map of plugins that are responsible for reading settings in service-specific way. Key is a service name supported
     * by plugin being a value.
     */
    protected Map<String, ISettingsReaderPlugin> settingsPluginsMap = new HashMap<String, ISettingsReaderPlugin>();

    /**
     * Creates reader for reading from files with declaration of test suite.
     * 
     * @param filename
     *            Name of file that will be read.
     */
    public TestsDeclarationReader(String filename) {
        this.filename = filename;

    }

    /**
     * Reads from file given in constructor. Returns content of that file.
     * 
     * @return Content of file given in constructor.
     * @throws IOException
     *             Thrown when some IO error happened during invocation of this method.
     * @throws FileNotFoundException
     *             Thrown when file given in constructor, doesn't exist.
     */
    protected String readFromFile() throws FileNotFoundException, IOException {

        StringBuilder builder = new StringBuilder();
        BufferedReader reader = new BufferedReader(new FileReader(filename));

        String currentLine = reader.readLine();
        while (currentLine != null) {
            builder.append(currentLine);
            currentLine = reader.readLine();
        }

        return builder.toString();
    }

    /**
     * Reads {@link TestsSuiteDeclaration} object from file given in constructor.
     * 
     * @return Object filled with data from file.
     * @throws IOException
     *             Thrown when there was an IO error during reading.
     * @throws FileNotFoundException
     *             Thrown when file given in constructor, wasn't found.
     * @throws TestSuiteFileException
     *             Thrown when content of file with description isn't proper.
     */
    public final TestsSuiteDeclaration readTestsSuiteDeclaration() throws FileNotFoundException, IOException,
            TestSuiteFileException {
        TestsSuiteDeclaration testSuite = abstractReadTestsSuiteDeclaration();

        testSuite.check();

        return testSuite;
    }

    /**
     * Abstract method to be defined by subclasses. It is used by {@link #readTestsSuiteDeclaration()} where it is
     * invoked.
     * 
     * @return Object filled with data from file.
     * @throws IOException
     *             Thrown when there was an IO error during reading.
     * @throws FileNotFoundException
     *             Thrown when file given in constructor, wasn't found.
     * @throws TestSuiteFileException
     *             Thrown when content of file with description isn't proper.
     */
    protected abstract TestsSuiteDeclaration abstractReadTestsSuiteDeclaration() throws FileNotFoundException,
            IOException, TestSuiteFileException;

    /**
     * Adds given plugin to reader, so that it will be used for reading settings in service-specific way.
     * 
     * @param plugin
     * @throws IllegalArgumentException
     *             Thrown when given plugin supports a service for which a plugin is already set.
     */
    public final void addSettingsReaderPlugin(ISettingsReaderPlugin plugin) {
        String pluginServiceName = plugin.getSupportedServiceName();
        if (settingsPluginsMap.containsKey(pluginServiceName)) {
            throw new IllegalArgumentException("Plugin for settings for service \"" + pluginServiceName
                    + "\" is already set.");
        }

        settingsPluginsMap.put(pluginServiceName, plugin);
    }
}
